#![allow(dead_code)]
#![allow(unused_variables)]

macro_rules! times_five {
    ($exp:expr) => {
        5 * $exp
    };
}

macro_rules! four {
    () => {
        1 + 3
    };
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_times_five() {
        let tv = 2 + times_five!(2);
        println!("{}", tv + four!());

        println!("vis========================================");

        macro_rules! item_test {
            // ident 标识符 (identifier)：包括关键字 (keywords)
            // vis 可视标识符：可能为空的可视标识符，比如 pub、pub(in crate)
            ($s_vis:vis $s_ident:ident $s_name:ident {
                $($f_vis:vis $f_name:ident : $f_ident:ident),* $(,)*
            }) => {
                println!("{} {} {} {{", stringify!($s_vis), stringify!($s_ident), stringify!($s_name));
                $(
                    println!("\t{} {} {}", stringify!($f_vis), stringify!($f_name), stringify!($f_ident));
                )+
                println!("}}");
            };
        }

        item_test!(
            pub struct Named {
                pub name: String,
                pub age: u8,
            }
        );

        println!("items========================================");

        // item 是在编译时完全确定的，通常在程序执行期间保持固定，并且可以驻留在只读存储器中。具体指：
        // modules
        // extern crate declarations
        // use declarations
        // function definitions
        // type definitions
        // struct definitions
        // enumeration definitions
        // union definitions
        // constant items
        // static items
        // trait definitions
        // implementations
        // extern blocks
        macro_rules! items {
            ($($item:item)*) => (
                $(
                    println!("{}", stringify!($item));
                )*
            );
        }

        items! {
            struct Foo;
            enum Bar {
                Baz
            }
            impl Foo {}
            /*...*/
            fn test() {
                println!("test");
            }
        }

        println!("statements========================================");

        // stat 分类符只匹配的 语句 (statement)。 除非 item 语句要求结尾有分号，否则 不会 匹配语句最后的分号。
        // 什么叫 item 语句要求结尾有分号呢？单元结构体 (Unit-Struct) 就是一个简单的例子， 因为它定义中必须带上结尾的分号。
        // 赶紧用例子展示上面说的是啥意思吧。下面的宏只给出它所捕获的内容，因为有几行不能通过编译。
        macro_rules! statements {
            ($($stmt:stmt)*) => {
                $(
                    println!("{}", stringify!($stmt));
                    $stmt
                )*
            };
        }

        statements! {
            struct Foo;
            // 这里可以生成一个新的函数
            fn foo() { println!("Generate a new function foo"); }
            let zig = 3
            let zig = 3
            if true {} else {}
            {}
        };

        foo();

        println!("patterns========================================");
        // pat 分类符用于匹配任何形式的模式 (pattern)。
        macro_rules! patterns {
            ($($pat:pat)*) => {
                $(
                    println!("{}", stringify!($pat));
                )*
            };
        }

        patterns! {
            "literal"
            _
            0..5
            ref mut PatternsAreNice
            okoko
        }

        println!("expressions========================================");
        // expr 分类符用于匹配任何形式的表达式 (expression)。
        // （如果把 Rust 视为面向表达式的语言，那么它有很多种表达式。）
        macro_rules! expressions {
            ($($expr:expr)*) => (
                $(
                    println!("{}", stringify!($expr));
                )*
            );
        }

        expressions! {
            "literal"
            funcall()
            future.await
            break 'foo bar
        }

        println!("types========================================");
        // ty 分类符用于匹配任何形式的类型表达式 (type expression)。
        // 类型表达式是在 Rust 中指代类型的语法
        macro_rules! types {
            ($($type:ty)*) => (
                $(
                    println!("{}", stringify!($type));
                )*
            );
        }

        types! {
            foo::bar
            bool
            [u8]
            le
        }

        println!("idents========================================");
        // ident 分类符用于匹配任何形式的标识符或者关键字。 (identifier)。
        macro_rules! idents {
            ($($ident:ident)*) => (
                $(
                    println!("{}", stringify!($ident));
                )*
            );
        }

        idents! {
            // _ /* `_` 不是标识符，而是一种模式 */
            foo
            async
            O_________O
            _____O_____
        }

        println!("metas========================================");
        // meta 分类符用于匹配属性 (attribute)， 准确地说是属性里面的内容。通常你会在 #[$meta:meta] 或 #![$meta:meta] 模式匹配中 看到这个分类符。
        macro_rules! metas {
            ($($meta:meta)*) => (
                $(
                    println!("{}", stringify!($meta));
                )*
            );
        }

        metas! {
            ASimplePath
            super::man
            path = "home"
            foo(bar)
        };

        println!("lifetime========================================");
        // lifetime 分类符用于匹配生命周期注解或者标签 (lifetime or label)。 它与 ident 很像，但是 lifetime 会匹配到前缀 '' 。
        macro_rules! lifetimes {
            ($($lifetime:lifetime)*) => (
                $(
                    println!("{}", stringify!($lifetime));
                )*
            );
        }

        lifetimes! {
            'static
            'shiv
            '_
        }

        println!("visibilities========================================");
        // vis 分类符会匹配 可能为空 的内容。 (Visibility qualifier)。 重点在于“可能为空”。
        // 你可能想到这是隐藏了 ? 重复操作符的分类符， 这样你就不用直接在反复匹配时使用 ? —— 其实你不能将它和 ? 一起在重复模式匹配中使用。
        macro_rules! visibilities {
            // 注意这个逗号，`vis` 分类符自身不会匹配到逗号
            ($($vis:vis,)*) => (
                $(
                    println!("{}", stringify!($vis));
                )*
            );
        }

        visibilities! {
            ,
            pub,
            pub(crate),
            pub(in super),
            pub(in some_path),
        }

        println!("literal========================================");
        // literal 分类符用于匹配字面表达式 (literal expression)。
        macro_rules! literals {
            ($($literal:literal)*) => (
                $(
                    println!("{}", stringify!($literal));
                )*
            );
        }

        literals! {
            -1
            "hello world"
            2.3
            b'b'
            true
        }
    }
}
